package cn.krui.helper.tools;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLEncoder;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import cn.hutool.http.useragent.UserAgent;
import cn.hutool.http.useragent.UserAgentUtil;

/**
 * @author zc
 */
public class WebToolsUtils {
  public static final String USER_ARGENT = "user-agent";
  
  public static String getClientIP() {
    HttpServletRequest request = getRequest();
    String proxy = request.getHeader("x-forwarded-for");
    if (proxy == null){
      return request.getRemoteAddr(); 
    }
    return proxy;
  }
  
  
  public static HttpRequestResult http(String urlStr, String method, byte[] data, Map<String, String> headers, Charset charset) {
    HttpRequestResult rr = new HttpRequestResult();
    HttpURLConnection conn = null;
    byte[] buffer = new byte[4096];
    try {
      URL url = new URL(urlStr);
      conn = (HttpURLConnection)url.openConnection();
      if (method != null && method.length() > 0){
        conn.setRequestMethod(method.toUpperCase()); 
      }
      if (headers != null){
        for (Map.Entry<String, String> header : headers.entrySet()){
          conn.addRequestProperty(header.getKey(), header.getValue());  
        }
      }
      if (data != null && data.length > 0) {
        conn.setDoOutput(true);
        conn.connect();
        OutputStream out = conn.getOutputStream();
        out.write(data);
        out.flush();
        out.close();
      } else {
        conn.connect();
      } 
      InputStream in = conn.getInputStream();
      ByteArrayOutputStream memStream = new ByteArrayOutputStream();
      int len = in.read(buffer);
      while (len > 0) {
        memStream.write(buffer, 0, len);
        len = in.read(buffer);
      } 
      in.close();
      rr.setResultCode(Integer.valueOf(conn.getResponseCode()));
      rr.setResultMsg(conn.getResponseMessage());
      rr.setResultContent(new String(memStream.toByteArray(), charset));
    } catch (Exception e) {
      if (conn != null) {
        try {
          InputStream errStream = conn.getErrorStream();
          if (errStream != null) {
            ByteArrayOutputStream memStream = new ByteArrayOutputStream();
            int len = errStream.read(buffer);
            while (len > 0) {
              memStream.write(buffer, 0, len);
              len = errStream.read(buffer);
            } 
            errStream.close();
            rr.setResultContent(new String(memStream.toByteArray(), charset));
          } 
          rr.setResultCode(Integer.valueOf(conn.getResponseCode()));
        } catch (Exception ex) {
          rr.setResultCode(Integer.valueOf(-500));
          rr.setResultMsg(e.getMessage());
        } 
      } else {
        rr.setResultCode(Integer.valueOf(-501));
        rr.setResultMsg(e.getMessage());
      } 
    } 
    return rr;
  }
  
  public static int http(String urlStr, String method, byte[] data, Map<String, String> headers, OutputStream outStream) throws IOException {
    HttpURLConnection conn = null;
    byte[] buffer = new byte[4096];
    try {
      URL url = new URL(urlStr);
      conn = (HttpURLConnection)url.openConnection();
      if (method != null && method.length() > 0){
        conn.setRequestMethod(method.toUpperCase()); 
      }
      if (headers != null){
        for (Map.Entry<String, String> header : headers.entrySet()){
          conn.addRequestProperty(header.getKey(), header.getValue());  
        }
      }
      if (data != null && data.length > 0) {
        conn.setDoOutput(true);
        conn.connect();
        OutputStream out = conn.getOutputStream();
        out.write(data);
        out.flush();
        out.close();
      } else {
        conn.connect();
      } 
      InputStream in = conn.getInputStream();
      int len = in.read(buffer);
      while (len > 0) {
        outStream.write(buffer, 0, len);
        len = in.read(buffer);
      } 
      in.close();
      return conn.getResponseCode();
    } catch (Exception e) {
      if (conn != null) {
        InputStream errStream = conn.getErrorStream();
        if (errStream != null) {
          int len = errStream.read(buffer);
          while (len > 0) {
            outStream.write(buffer, 0, len);
            len = errStream.read(buffer);
          } 
          errStream.close();
        } 
        return conn.getResponseCode();
      } 
      return 500;
    } 
  }
  
  public static String httpGet(String urlStr) {
    return httpGet(urlStr, (String)null, true);
  }
  
  public static String httpGet(String urlStr, Map<String, Object> params, Map<String, String> headers) {
    if (urlStr.lastIndexOf('?') > 0) {
      if (!urlStr.endsWith("&")){
        urlStr = String.valueOf(urlStr) + "&"; 
      }
      urlStr = String.valueOf(urlStr) + urlEncode(params);
    } else {
      urlStr = String.valueOf(urlStr) + "?" + urlEncode(params);
    } 
    return httpGet(urlStr, headers);
  }
  
  public static String httpGet(String urlStr, String accept, boolean keepAlive) {
    Map<String, String> headers = new HashMap<>();
    if (accept != null && accept.length() > 0) {
      headers.put("accept", accept);
    } else {
      headers.put("accept", "*/*");
    } 
    if (keepAlive){
      headers.put("connection", "Keep-Alive"); 
    }
    return http(urlStr, "GET", (byte[])null, headers, StandardCharsets.UTF_8).getResultContent();
  }
  
  public static String httpGet(String urlStr, Map<String, String> headers) {
    return http(urlStr, "GET", (byte[])null, headers, StandardCharsets.UTF_8).getResultContent();
  }
  
  public static String urlEncode(Map<String, Object> params) {
    try {
      if (params != null) {
        StringBuilder form = new StringBuilder();
        for (Map.Entry<String, Object> entry : params.entrySet()) {
          if (form.length() > 0){
            form.append("&"); 
          }
          form.append(URLEncoder.encode(entry.getKey(), "UTF-8"))
            .append("=")
            .append(URLEncoder.encode((entry.getValue() == null) ? "" : entry.getValue().toString(), "UTF-8"));
        } 
        return form.toString();
      } 
      return "";
    } catch (Exception e) {
      return null;
    } 
  }
  
  public static String httpPost(String urlStr, Map<String, Object> forms) {
    return httpPost(urlStr, forms, null, false);
  }
  
  public static String httpPost(String urlStr, Map<String, Object> forms, String accept, boolean keepAlive) {
    try {
      Map<String, String> headers = new HashMap<>();
      if (accept != null && accept.length() > 0) {
        headers.put("accept", accept);
      } else {
        headers.put("accept", "*/*");
      } 
      if (keepAlive){
        headers.put("connection", "Keep-Alive"); 
      }
      headers.put("Charset", "UTF-8");
      headers.put("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
      return http(urlStr, "POST", urlEncode(forms).getBytes(StandardCharsets.UTF_8), headers, StandardCharsets.UTF_8).getResultContent();
    } catch (Exception e) {
      return null;
    } 
  }
  
  public static String httpPost(String urlStr, Map<String, Object> forms, Map<String, String> headers) {
    try {
      if (headers != null) {
        if (!headers.containsKey("Content-Type")){
          headers.put("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8"); 
        }
      } else {
        headers = new HashMap<>();
        headers.put("Charset", "UTF-8");
        headers.put("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
      } 
      return http(urlStr, "POST", urlEncode(forms).getBytes(StandardCharsets.UTF_8), headers, StandardCharsets.UTF_8).getResultContent();
    } catch (Exception e) {
      return null;
    } 
  }
  
  public static String httpPost(String urlStr, String json) {
    Map<String, String> headers = new HashMap<>();
    headers.put("Charset", "UTF-8");
    headers.put("Content-Type", "application/json; charset=UTF-8");
    return http(urlStr, "POST", json.getBytes(StandardCharsets.UTF_8), headers, StandardCharsets.UTF_8).getResultContent();
  }
  
  public static String httpPost(String urlStr, String json, Map<String, String> headers) {
    if (headers == null){
      headers = new HashMap<>(); 
    }
    if (!headers.containsKey("Content-Type")){
      headers.put("Content-Type", "application/json; charset=UTF-8"); 
    }
    return httpPost(urlStr, json);
  }
  
  public static String getClient() {
     UserAgent uAgent = UserAgentUtil.parse(getUserAgent());
    return uAgent.getOs().getName() + "/" + uAgent.getBrowser().getName();
  }
  
  public static String getUserAgent() {
    HttpServletRequest request = getRequest();
    return request.getHeader("user-agent");
  }
  
  public static HttpServletRequest getRequest() {
    return ((ServletRequestAttributes)RequestContextHolder.currentRequestAttributes()).getRequest();
  }
  
  public static HttpServletResponse getResponse() {
    return ((ServletRequestAttributes)RequestContextHolder.currentRequestAttributes()).getResponse();
  }
  
  public static HttpSession getSession() {
    return ((ServletRequestAttributes)RequestContextHolder.currentRequestAttributes()).getRequest().getSession();
  }
  
  public static HttpSession getSession(boolean create) {
    return ((ServletRequestAttributes)RequestContextHolder.currentRequestAttributes()).getRequest().getSession(create);
  }
  
  public static String getUriLeftPart(String uriStr, UriPartial part) throws URISyntaxException {
    String query;
    URI uri = new URI(uriStr);
    StringBuilder sb = new StringBuilder();
    String scheme = uri.getScheme();
    if (scheme != null) {
      sb.append(scheme).append(":");
    }
    if (uri.getAuthority() != null) {
      sb.append("//");
    }
    switch (part) {
      case Authority:
        sb.append(uri.getAuthority());
        break;
      case Path:
        sb.append(uri.getAuthority()).append(uri.getPath());
        break;
      case Query:
        sb.append(uri.getAuthority()).append(uri.getPath());
        query = uri.getQuery();
        if (query != null) {
          sb.append("?").append(query);
        }
        break;
      default:
        break;
    }
    return sb.toString();
  }
}
